{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "authorship_tag": "ABX9TyMRSMsRkWsziRv4OswbeL5E",
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/vitaldb/examples/blob/master/table1.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install fexact\n",
        "import fexact  # https://github.com/boussoffara/fexact\n",
        "\n",
        "def format_pval(pval):\n",
        "    '''Returns the formated string of a p-value'''\n",
        "    if pval < 0.001:\n",
        "        return '< 0.001'\n",
        "    return f'{pval:.3f}'\n",
        "\n",
        "def format_number(num, prec=3):\n",
        "    '''Returns the formated number up to specific precision'''\n",
        "    fmt = '{:,.' + str(prec) + 'f}'\n",
        "    s = fmt.format(num)\n",
        "    return s.rstrip('0').rstrip('.')\n",
        "\n",
        "def fisher_exact(table):\n",
        "    '''Returns p-value for the Fisher's exact test of nxm contingency table\n",
        "    fisher_exact([[8,2,12], [1,5,2]])  # 0.011825369366598752\n",
        "    '''\n",
        "    return fexact.fexact(np.array(table))\n",
        "\n",
        "print(format_pval(1e-5)) # 1e-5 == 1 * (10 ** -5) = 0.00001\n",
        "print(format_number(6388))"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "0D7TU0_C0_MI",
        "outputId": "291d84cf-b694-4cfb-defb-f93f7cd85679"
      },
      "execution_count": 20,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Requirement already satisfied: fexact in /usr/local/lib/python3.10/dist-packages (0.1.0)\n",
            "< 0.001\n",
            "6,388\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import warnings\n",
        "import numpy as np\n",
        "import scipy.stats as stat\n",
        "\n",
        "def table_one(df, grpvar=None, catvars=None):\n",
        "    # convert boolean type to int --> remains are float or str\n",
        "    df.replace({False: 0, True: 1}, inplace=True)\n",
        "\n",
        "    if grpvar is not None:\n",
        "        grp_names = np.unique(df[grpvar])\n",
        "\n",
        "    # for csv file\n",
        "    rows = []\n",
        "\n",
        "    # Generate table header\n",
        "    tabs = ['', 'Total']\n",
        "    if grpvar is not None:\n",
        "        for grp_name in grp_names:\n",
        "            tabs.append(f'{grpvar}={grp_name}')\n",
        "        if len(grp_names) > 1:\n",
        "            tabs.append('P-value')\n",
        "            tabs.append('Test')\n",
        "    rows.append(tabs)\n",
        "\n",
        "    tabs = ['n']\n",
        "    if grpvar is not None:\n",
        "        tabs.append(format_number(sum(~df[grpvar].isnull())))\n",
        "        for grp_name in grp_names:\n",
        "            tabs.append(format_number(sum(df[grpvar] == grp_name)) + ' (' + format_number(np.mean(df[grpvar] == grp_name) * 100, 1) + '%)')\n",
        "    else:\n",
        "        tabs.append(format_number(len(df)))\n",
        "    rows.append(tabs)\n",
        "\n",
        "    # Generate statistics for each variable\n",
        "    for col in df.columns:\n",
        "        if col == grpvar:\n",
        "            continue\n",
        "        try:\n",
        "            pd.to_numeric(df[col])\n",
        "            isstr = False\n",
        "        except:\n",
        "            isstr = True\n",
        "\n",
        "        unique_values = sorted(df.loc[~df[col].isnull(), col].unique())  # unique values\n",
        "\n",
        "        iscat = len(unique_values) < 8\n",
        "        if catvars:\n",
        "            if col in catvars:\n",
        "                iscat = True\n",
        "\n",
        "        if isstr and not iscat:\n",
        "            continue\n",
        "\n",
        "        if iscat:  # categorical variables --> represents as count (percent)\n",
        "            if grpvar is not None:  # create cross table (value x grp)\n",
        "                xtab = pd.crosstab(df[col], df[grpvar]).fillna(0)\n",
        "                pval = None\n",
        "                if len(grp_names) > 1: # NEJM requires Exact method for all categorical variables\n",
        "                    if (xtab > 5).all(axis=None):  # if there is an incidence < 5\n",
        "                        pval = stat.chi2_contingency(xtab)[1]\n",
        "                        test_name = 'Chi-square'\n",
        "                    else:\n",
        "                        pval = fisher_exact(xtab.T.values)\n",
        "                        test_name = 'Fisher\\'s exact'\n",
        "\n",
        "            is_binary = (len(unique_values) == 2) and (unique_values[0] == 0 and unique_values[1] == 1)\n",
        "            if is_binary:  # binary\n",
        "                # print total\n",
        "                tabs = [col, format_number(sum(df[col] == 1)) + ' (' + format_number(np.mean(df[col] == 1) * 100, 1) + '%)']\n",
        "                if grpvar is not None: # print group values\n",
        "                    for grp_name in grp_names:\n",
        "                        grp_mask = (df[grpvar] == grp_name)\n",
        "                        tabs.append(format_number(sum(df.loc[grp_mask, col])) + ' (' + format_number(np.mean(df.loc[grp_mask, col])*100, 1) + '%)')\n",
        "                    if pval is not None:\n",
        "                        tabs.append(format_pval(pval))\n",
        "                        tabs.append(test_name)\n",
        "                rows.append(tabs)\n",
        "            else:\n",
        "                for uval in unique_values:\n",
        "                    # print total\n",
        "                    tabs = [f'{col}={uval}', format_number(sum(df[col] == uval)) + ' (' + format_number(np.mean(df[col] == uval) * 100, 1) + '%)']\n",
        "                    if grpvar is not None: # print group values\n",
        "                        for grp_name in grp_names:\n",
        "                            grp_mask = (df[grpvar] == grp_name)\n",
        "                            tabs.append(format_number(sum(df.loc[grp_mask, col] == uval)) + ' (' + format_number(np.mean(df.loc[grp_mask, col] == uval) * 100, 1) + '%)')\n",
        "                        if pval is not None:\n",
        "                            if uval == unique_values[0]:\n",
        "                                tabs.append(format_pval(pval))\n",
        "                                tabs.append(test_name)\n",
        "                    rows.append(tabs)\n",
        "\n",
        "        else:  # continuous variables --> represents as mean (SD)\n",
        "            with warnings.catch_warnings():\n",
        "                warnings.simplefilter(\"ignore\")\n",
        "                isnorm = stat.shapiro(df[col])[1] > 0.05  # check if it is normal distribution\n",
        "            if isnorm:  # normal distribution\n",
        "                # print total\n",
        "                m = df[col].mean()\n",
        "                s = df[col].std()\n",
        "                tabs = [col, f'{m:.3f} ({s:.3f})']\n",
        "\n",
        "                if grpvar is not None:\n",
        "                    # extract group values\n",
        "                    grp_vals = []\n",
        "                    for grp_name in grp_names:\n",
        "                        a = df.loc[df[grpvar] == grp_name, col]\n",
        "                        grp_vals.append(a[~a.isnull()])\n",
        "\n",
        "                    # print group values\n",
        "                    for igrp in range(len(grp_vals)):\n",
        "                        m = grp_vals[igrp].mean()\n",
        "                        s = grp_vals[igrp].std()\n",
        "                        tabs.append(f'{m:.3f} ({s:.3f})')\n",
        "\n",
        "                    # print stats\n",
        "                    if len(grp_names) == 2:\n",
        "                        equal_var = stat.levene(grp_vals[0], grp_vals[1])[1] > 0.05  # levene\n",
        "                        pval = stat.ttest_ind(grp_vals[0], grp_vals[1], equal_var=equal_var)[1]\n",
        "                        test_name = 'T-test'\n",
        "                    else:  # 3 or more groups -> anova\n",
        "                        equal_var = stat.levene(*grp_vals)[1] > 0.05  # levene + homoscedasticity\n",
        "                        if equal_var:\n",
        "                            pval = stat.f_oneway(*grp_vals)[1]\n",
        "                            test_name = 'One-way ANOVA'\n",
        "                        else:\n",
        "                            pval = stat.kruskal(*grp_vals)[1]\n",
        "                            test_name = 'Kruskal-Wallis'\n",
        "                        tabs.append(format_pval(pval))\n",
        "                        tabs.append(test_name)\n",
        "            else:  # non-normal\n",
        "                # print total\n",
        "                m = df[col].median()\n",
        "                q1 = df[col].quantile(0.25)\n",
        "                q2 = df[col].quantile(0.75)\n",
        "                tabs = [col, format_number(m, 3) + ' (' + format_number(q1, 3) + '-' + format_number(q2, 3) + ')']\n",
        "\n",
        "                if grpvar is not None:\n",
        "                    # extract group values\n",
        "                    grp_vals = []\n",
        "                    for grp_name in grp_names:\n",
        "                        a = df.loc[df[grpvar] == grp_name, col]\n",
        "                        grp_vals.append(a[~a.isnull()])\n",
        "\n",
        "                    # print group value\n",
        "                    for igrp in range(len(grp_vals)):\n",
        "                        m = grp_vals[igrp].median()\n",
        "                        q1 = grp_vals[igrp].quantile(0.25)\n",
        "                        q2 = grp_vals[igrp].quantile(0.75)\n",
        "                        tabs.append(format_number(m, 3) + ' (' + format_number(q1, 3) + '-' + format_number(q2, 3) + ')')\n",
        "\n",
        "                    # print stats\n",
        "                    if len(grp_vals) == 2:\n",
        "                        pval = stat.mannwhitneyu(grp_vals[0], grp_vals[1], alternative='two-sided')[1]\n",
        "                        test_name = 'Mann-Whitney'\n",
        "                    elif len(grp_vals) > 2:  # > 3 groups\n",
        "                        pval = stat.kruskal(*grp_vals)[1]\n",
        "                        test_name = 'Kruskal-Wallis'\n",
        "                    tabs.append(format_pval(pval))\n",
        "                    tabs.append(test_name)\n",
        "\n",
        "            rows.append(tabs)\n",
        "\n",
        "    return pd.DataFrame(rows)"
      ],
      "metadata": {
        "id": "skxasGJg1XXo"
      },
      "execution_count": 21,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "import pandas as pd\n",
        "\n",
        "# read data\n",
        "df = pd.read_csv('https://api.vitaldb.net/cases')\n",
        "df.info()"
      ],
      "metadata": {
        "id": "TAiK48CIjwtg",
        "outputId": "99890d38-ae40-409d-e343-d6e9edda2ff0",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 22,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "<class 'pandas.core.frame.DataFrame'>\n",
            "RangeIndex: 6388 entries, 0 to 6387\n",
            "Data columns (total 74 columns):\n",
            " #   Column               Non-Null Count  Dtype  \n",
            "---  ------               --------------  -----  \n",
            " 0   caseid               6388 non-null   int64  \n",
            " 1   subjectid            6388 non-null   int64  \n",
            " 2   casestart            6388 non-null   int64  \n",
            " 3   caseend              6388 non-null   int64  \n",
            " 4   anestart             6388 non-null   int64  \n",
            " 5   aneend               6388 non-null   float64\n",
            " 6   opstart              6388 non-null   int64  \n",
            " 7   opend                6388 non-null   int64  \n",
            " 8   adm                  6388 non-null   int64  \n",
            " 9   dis                  6388 non-null   int64  \n",
            " 10  icu_days             6388 non-null   int64  \n",
            " 11  death_inhosp         6388 non-null   int64  \n",
            " 12  age                  6388 non-null   float64\n",
            " 13  sex                  6388 non-null   object \n",
            " 14  height               6388 non-null   float64\n",
            " 15  weight               6388 non-null   float64\n",
            " 16  bmi                  6388 non-null   float64\n",
            " 17  asa                  6255 non-null   float64\n",
            " 18  emop                 6388 non-null   int64  \n",
            " 19  department           6388 non-null   object \n",
            " 20  optype               6388 non-null   object \n",
            " 21  dx                   6388 non-null   object \n",
            " 22  opname               6388 non-null   object \n",
            " 23  approach             6388 non-null   object \n",
            " 24  position             6194 non-null   object \n",
            " 25  ane_type             6388 non-null   object \n",
            " 26  preop_htn            6388 non-null   int64  \n",
            " 27  preop_dm             6388 non-null   int64  \n",
            " 28  preop_ecg            6388 non-null   object \n",
            " 29  preop_pft            6388 non-null   object \n",
            " 30  preop_hb             6047 non-null   float64\n",
            " 31  preop_plt            6047 non-null   float64\n",
            " 32  preop_pt             5998 non-null   float64\n",
            " 33  preop_aptt           5986 non-null   float64\n",
            " 34  preop_na             5765 non-null   float64\n",
            " 35  preop_k              5767 non-null   float64\n",
            " 36  preop_gluc           6010 non-null   float64\n",
            " 37  preop_alb            6016 non-null   float64\n",
            " 38  preop_ast            6022 non-null   float64\n",
            " 39  preop_alt            6024 non-null   float64\n",
            " 40  preop_bun            6023 non-null   float64\n",
            " 41  preop_cr             6016 non-null   float64\n",
            " 42  preop_ph             546 non-null    float64\n",
            " 43  preop_hco3           533 non-null    float64\n",
            " 44  preop_be             532 non-null    float64\n",
            " 45  preop_pao2           538 non-null    float64\n",
            " 46  preop_paco2          538 non-null    float64\n",
            " 47  preop_sao2           533 non-null    float64\n",
            " 48  cormack              5553 non-null   object \n",
            " 49  airway               5973 non-null   object \n",
            " 50  tubesize             4919 non-null   float64\n",
            " 51  dltubesize           930 non-null    object \n",
            " 52  lmasize              102 non-null    float64\n",
            " 53  iv1                  6313 non-null   object \n",
            " 54  iv2                  1437 non-null   object \n",
            " 55  aline1               3463 non-null   object \n",
            " 56  aline2               105 non-null    object \n",
            " 57  cline1               1541 non-null   object \n",
            " 58  cline2               60 non-null     object \n",
            " 59  intraop_ebl          3987 non-null   float64\n",
            " 60  intraop_uo           3707 non-null   float64\n",
            " 61  intraop_rbc          6388 non-null   int64  \n",
            " 62  intraop_ffp          6388 non-null   int64  \n",
            " 63  intraop_crystalloid  5980 non-null   float64\n",
            " 64  intraop_colloid      6388 non-null   int64  \n",
            " 65  intraop_ppf          6388 non-null   int64  \n",
            " 66  intraop_mdz          6388 non-null   float64\n",
            " 67  intraop_ftn          6388 non-null   int64  \n",
            " 68  intraop_rocu         6388 non-null   int64  \n",
            " 69  intraop_vecu         6388 non-null   int64  \n",
            " 70  intraop_eph          6388 non-null   int64  \n",
            " 71  intraop_phe          6388 non-null   int64  \n",
            " 72  intraop_epi          6388 non-null   int64  \n",
            " 73  intraop_ca           6388 non-null   int64  \n",
            "dtypes: float64(30), int64(25), object(19)\n",
            "memory usage: 3.6+ MB\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "\n",
        "# add columns\n",
        "df['opdur'] = df['opend'] - df['opstart']\n",
        "df['anedur'] = df['aneend'] - df['anestart']\n",
        "df['hospdur'] = df['dis'] - df['adm']\n",
        "\n",
        "# remove columns\n",
        "df.drop(columns=['opstart', 'opend', 'anestart', 'aneend', 'dis', 'adm', 'caseid'], inplace=True)\n",
        "df = df.loc[:, ~df.columns.str.endswith('id')]\n",
        "\n",
        "# create table one\n",
        "df_results = table_one(df, 'death_inhosp', ['department'])\n",
        "\n",
        "# save and print results\n",
        "df_results.to_csv('table1.csv', index=False, header=False)\n",
        "df_results"
      ],
      "metadata": {
        "id": "Ioers-NI1Utr",
        "outputId": "d98b1b0b-8209-46bc-f47c-827f03cf0306",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 614
        }
      },
      "execution_count": 23,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "              0                            1                          2  \\\n",
              "0                                      Total             death_inhosp=0   \n",
              "1             n                        6,388              6,331 (99.1%)   \n",
              "2   casestart=0                 6,388 (100%)               6,331 (100%)   \n",
              "3       caseend  9,924.5 (6,194.5-15,072.75)       9,930 (6,189-15,053)   \n",
              "4      icu_days                      0 (0-0)                    0 (0-0)   \n",
              "..          ...                          ...                        ...   \n",
              "74  intraop_epi                      0 (0-0)                    0 (0-0)   \n",
              "75   intraop_ca                      0 (0-0)                    0 (0-0)   \n",
              "76        opdur         6,600 (3,600-11,400)       6,600 (3,600-11,400)   \n",
              "77       anedur        10,500 (6,720-15,600)      10,500 (6,690-15,600)   \n",
              "78      hospdur    604,800 (345,600-950,400)  604,800 (345,600-950,400)   \n",
              "\n",
              "                                3        4             5  \n",
              "0                  death_inhosp=1  P-value          Test  \n",
              "1                       57 (0.9%)     None          None  \n",
              "2                       57 (100%)    1.000    Chi-square  \n",
              "3            9,786 (6,655-16,200)    0.380  Mann-Whitney  \n",
              "4                        1 (0-16)  < 0.001  Mann-Whitney  \n",
              "..                            ...      ...           ...  \n",
              "74                        0 (0-0)  < 0.001  Mann-Whitney  \n",
              "75                      0 (0-300)  < 0.001  Mann-Whitney  \n",
              "76           6,600 (3,900-12,243)    0.423  Mann-Whitney  \n",
              "77          10,800 (7,140-16,500)    0.311  Mann-Whitney  \n",
              "78  1,728,000 (691,200-4,665,600)  < 0.001  Mann-Whitney  \n",
              "\n",
              "[79 rows x 6 columns]"
            ],
            "text/html": [
              "\n",
              "  <div id=\"df-341de137-b0dd-4a97-8b53-25a5bb382266\" class=\"colab-df-container\">\n",
              "    <div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>0</th>\n",
              "      <th>1</th>\n",
              "      <th>2</th>\n",
              "      <th>3</th>\n",
              "      <th>4</th>\n",
              "      <th>5</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td></td>\n",
              "      <td>Total</td>\n",
              "      <td>death_inhosp=0</td>\n",
              "      <td>death_inhosp=1</td>\n",
              "      <td>P-value</td>\n",
              "      <td>Test</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>n</td>\n",
              "      <td>6,388</td>\n",
              "      <td>6,331 (99.1%)</td>\n",
              "      <td>57 (0.9%)</td>\n",
              "      <td>None</td>\n",
              "      <td>None</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>casestart=0</td>\n",
              "      <td>6,388 (100%)</td>\n",
              "      <td>6,331 (100%)</td>\n",
              "      <td>57 (100%)</td>\n",
              "      <td>1.000</td>\n",
              "      <td>Chi-square</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>caseend</td>\n",
              "      <td>9,924.5 (6,194.5-15,072.75)</td>\n",
              "      <td>9,930 (6,189-15,053)</td>\n",
              "      <td>9,786 (6,655-16,200)</td>\n",
              "      <td>0.380</td>\n",
              "      <td>Mann-Whitney</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>icu_days</td>\n",
              "      <td>0 (0-0)</td>\n",
              "      <td>0 (0-0)</td>\n",
              "      <td>1 (0-16)</td>\n",
              "      <td>&lt; 0.001</td>\n",
              "      <td>Mann-Whitney</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>...</th>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>74</th>\n",
              "      <td>intraop_epi</td>\n",
              "      <td>0 (0-0)</td>\n",
              "      <td>0 (0-0)</td>\n",
              "      <td>0 (0-0)</td>\n",
              "      <td>&lt; 0.001</td>\n",
              "      <td>Mann-Whitney</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>75</th>\n",
              "      <td>intraop_ca</td>\n",
              "      <td>0 (0-0)</td>\n",
              "      <td>0 (0-0)</td>\n",
              "      <td>0 (0-300)</td>\n",
              "      <td>&lt; 0.001</td>\n",
              "      <td>Mann-Whitney</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>76</th>\n",
              "      <td>opdur</td>\n",
              "      <td>6,600 (3,600-11,400)</td>\n",
              "      <td>6,600 (3,600-11,400)</td>\n",
              "      <td>6,600 (3,900-12,243)</td>\n",
              "      <td>0.423</td>\n",
              "      <td>Mann-Whitney</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>77</th>\n",
              "      <td>anedur</td>\n",
              "      <td>10,500 (6,720-15,600)</td>\n",
              "      <td>10,500 (6,690-15,600)</td>\n",
              "      <td>10,800 (7,140-16,500)</td>\n",
              "      <td>0.311</td>\n",
              "      <td>Mann-Whitney</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>78</th>\n",
              "      <td>hospdur</td>\n",
              "      <td>604,800 (345,600-950,400)</td>\n",
              "      <td>604,800 (345,600-950,400)</td>\n",
              "      <td>1,728,000 (691,200-4,665,600)</td>\n",
              "      <td>&lt; 0.001</td>\n",
              "      <td>Mann-Whitney</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "<p>79 rows × 6 columns</p>\n",
              "</div>\n",
              "    <div class=\"colab-df-buttons\">\n",
              "\n",
              "  <div class=\"colab-df-container\">\n",
              "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-341de137-b0dd-4a97-8b53-25a5bb382266')\"\n",
              "            title=\"Convert this dataframe to an interactive table.\"\n",
              "            style=\"display:none;\">\n",
              "\n",
              "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
              "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
              "  </svg>\n",
              "    </button>\n",
              "\n",
              "  <style>\n",
              "    .colab-df-container {\n",
              "      display:flex;\n",
              "      gap: 12px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert {\n",
              "      background-color: #E8F0FE;\n",
              "      border: none;\n",
              "      border-radius: 50%;\n",
              "      cursor: pointer;\n",
              "      display: none;\n",
              "      fill: #1967D2;\n",
              "      height: 32px;\n",
              "      padding: 0 0 0 0;\n",
              "      width: 32px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert:hover {\n",
              "      background-color: #E2EBFA;\n",
              "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "      fill: #174EA6;\n",
              "    }\n",
              "\n",
              "    .colab-df-buttons div {\n",
              "      margin-bottom: 4px;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert {\n",
              "      background-color: #3B4455;\n",
              "      fill: #D2E3FC;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert:hover {\n",
              "      background-color: #434B5C;\n",
              "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
              "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
              "      fill: #FFFFFF;\n",
              "    }\n",
              "  </style>\n",
              "\n",
              "    <script>\n",
              "      const buttonEl =\n",
              "        document.querySelector('#df-341de137-b0dd-4a97-8b53-25a5bb382266 button.colab-df-convert');\n",
              "      buttonEl.style.display =\n",
              "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "\n",
              "      async function convertToInteractive(key) {\n",
              "        const element = document.querySelector('#df-341de137-b0dd-4a97-8b53-25a5bb382266');\n",
              "        const dataTable =\n",
              "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
              "                                                    [key], {});\n",
              "        if (!dataTable) return;\n",
              "\n",
              "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
              "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
              "          + ' to learn more about interactive tables.';\n",
              "        element.innerHTML = '';\n",
              "        dataTable['output_type'] = 'display_data';\n",
              "        await google.colab.output.renderOutput(dataTable, element);\n",
              "        const docLink = document.createElement('div');\n",
              "        docLink.innerHTML = docLinkHtml;\n",
              "        element.appendChild(docLink);\n",
              "      }\n",
              "    </script>\n",
              "  </div>\n",
              "\n",
              "\n",
              "<div id=\"df-44beff11-3f78-4080-8ecc-23be9647d2e2\">\n",
              "  <button class=\"colab-df-quickchart\" onclick=\"quickchart('df-44beff11-3f78-4080-8ecc-23be9647d2e2')\"\n",
              "            title=\"Suggest charts.\"\n",
              "            style=\"display:none;\">\n",
              "\n",
              "<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
              "     width=\"24px\">\n",
              "    <g>\n",
              "        <path d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z\"/>\n",
              "    </g>\n",
              "</svg>\n",
              "  </button>\n",
              "\n",
              "<style>\n",
              "  .colab-df-quickchart {\n",
              "      --bg-color: #E8F0FE;\n",
              "      --fill-color: #1967D2;\n",
              "      --hover-bg-color: #E2EBFA;\n",
              "      --hover-fill-color: #174EA6;\n",
              "      --disabled-fill-color: #AAA;\n",
              "      --disabled-bg-color: #DDD;\n",
              "  }\n",
              "\n",
              "  [theme=dark] .colab-df-quickchart {\n",
              "      --bg-color: #3B4455;\n",
              "      --fill-color: #D2E3FC;\n",
              "      --hover-bg-color: #434B5C;\n",
              "      --hover-fill-color: #FFFFFF;\n",
              "      --disabled-bg-color: #3B4455;\n",
              "      --disabled-fill-color: #666;\n",
              "  }\n",
              "\n",
              "  .colab-df-quickchart {\n",
              "    background-color: var(--bg-color);\n",
              "    border: none;\n",
              "    border-radius: 50%;\n",
              "    cursor: pointer;\n",
              "    display: none;\n",
              "    fill: var(--fill-color);\n",
              "    height: 32px;\n",
              "    padding: 0;\n",
              "    width: 32px;\n",
              "  }\n",
              "\n",
              "  .colab-df-quickchart:hover {\n",
              "    background-color: var(--hover-bg-color);\n",
              "    box-shadow: 0 1px 2px rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "    fill: var(--button-hover-fill-color);\n",
              "  }\n",
              "\n",
              "  .colab-df-quickchart-complete:disabled,\n",
              "  .colab-df-quickchart-complete:disabled:hover {\n",
              "    background-color: var(--disabled-bg-color);\n",
              "    fill: var(--disabled-fill-color);\n",
              "    box-shadow: none;\n",
              "  }\n",
              "\n",
              "  .colab-df-spinner {\n",
              "    border: 2px solid var(--fill-color);\n",
              "    border-color: transparent;\n",
              "    border-bottom-color: var(--fill-color);\n",
              "    animation:\n",
              "      spin 1s steps(1) infinite;\n",
              "  }\n",
              "\n",
              "  @keyframes spin {\n",
              "    0% {\n",
              "      border-color: transparent;\n",
              "      border-bottom-color: var(--fill-color);\n",
              "      border-left-color: var(--fill-color);\n",
              "    }\n",
              "    20% {\n",
              "      border-color: transparent;\n",
              "      border-left-color: var(--fill-color);\n",
              "      border-top-color: var(--fill-color);\n",
              "    }\n",
              "    30% {\n",
              "      border-color: transparent;\n",
              "      border-left-color: var(--fill-color);\n",
              "      border-top-color: var(--fill-color);\n",
              "      border-right-color: var(--fill-color);\n",
              "    }\n",
              "    40% {\n",
              "      border-color: transparent;\n",
              "      border-right-color: var(--fill-color);\n",
              "      border-top-color: var(--fill-color);\n",
              "    }\n",
              "    60% {\n",
              "      border-color: transparent;\n",
              "      border-right-color: var(--fill-color);\n",
              "    }\n",
              "    80% {\n",
              "      border-color: transparent;\n",
              "      border-right-color: var(--fill-color);\n",
              "      border-bottom-color: var(--fill-color);\n",
              "    }\n",
              "    90% {\n",
              "      border-color: transparent;\n",
              "      border-bottom-color: var(--fill-color);\n",
              "    }\n",
              "  }\n",
              "</style>\n",
              "\n",
              "  <script>\n",
              "    async function quickchart(key) {\n",
              "      const quickchartButtonEl =\n",
              "        document.querySelector('#' + key + ' button');\n",
              "      quickchartButtonEl.disabled = true;  // To prevent multiple clicks.\n",
              "      quickchartButtonEl.classList.add('colab-df-spinner');\n",
              "      try {\n",
              "        const charts = await google.colab.kernel.invokeFunction(\n",
              "            'suggestCharts', [key], {});\n",
              "      } catch (error) {\n",
              "        console.error('Error during call to suggestCharts:', error);\n",
              "      }\n",
              "      quickchartButtonEl.classList.remove('colab-df-spinner');\n",
              "      quickchartButtonEl.classList.add('colab-df-quickchart-complete');\n",
              "    }\n",
              "    (() => {\n",
              "      let quickchartButtonEl =\n",
              "        document.querySelector('#df-44beff11-3f78-4080-8ecc-23be9647d2e2 button');\n",
              "      quickchartButtonEl.style.display =\n",
              "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "    })();\n",
              "  </script>\n",
              "</div>\n",
              "    </div>\n",
              "  </div>\n"
            ]
          },
          "metadata": {},
          "execution_count": 23
        }
      ]
    }
  ]
}